package com.restphp.java.utils.ftp;

import com.restphp.java.utils.string.core.StringUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * FTP请求工具
 * Created by sofical on 2020/12/17.
 */
public class FtpClientUtils {
    private FtpClientLogger ftpLogger = null;
    private FTPClient ftpClient = null;
    private FtpConfig ftpConfig = null;
    public FtpClientUtils(FtpConfig ftpConfig) {
        this.ftpConfig = ftpConfig;
        this.ftpClient = new FTPClient();
        this.connect();
    }
    public FtpClientUtils(FtpConfig ftpConfig, FtpClientLogger ftpLogger) {
        this.ftpConfig = ftpConfig;
        this.ftpClient = new FTPClient();
        this.ftpLogger = ftpLogger;
        this.connect();
    }

    /**
     * 链接ftp.
     */
    public void connect() {
        ftpClient.setControlEncoding(this.ftpConfig.getEncode());
        try {
            ftpClient.connect(this.ftpConfig.getHost(), this.ftpConfig.getPort());
            ftpClient.login(this.ftpConfig.getUsername(), this.ftpConfig.getPassword());
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
            }
            ftpClient.sendCommand("OPTS " + this.ftpConfig.getEncode(), "ON");
            this.log("connect", ftpClient.getReplyCode(), ftpClient.getReplyString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 释放链接.
     */
    public void disconnect() {
        if (null != ftpClient) {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 写文件.
     * @param path 路径.
     * @param fileName 文件名称.
     * @param content 内容.
     */
    public void write(String path, String fileName, String content) {
        InputStream in = null;
        try {
            this.mkDirDeep(path);
            ftpClient.changeWorkingDirectory(path);
            System.out.println(ftpClient.printWorkingDirectory());

            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            if (this.ftpConfig.getMode().equals(FtpMode.ACTIVE)) {
                ftpClient.enterLocalActiveMode();
            } else {
                ftpClient.enterLocalPassiveMode();
            }
            in = new ByteArrayInputStream(content.getBytes(this.ftpConfig.getEncode()));
            if (!ftpClient.storeFile(fileName, in)) {
                System.out.println(ftpClient.getReplyCode());
                System.out.println(ftpClient.getReplyString());
            }
            //ftpClient.completePendingCommand();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 深度创建目录.
     * @param path 需要创建的目录.
     */
    public void mkDirDeep(String path) {
        String[] pathArr = path.split("/");
        String current = this.ftpConfig.getFtpRoot();
        for (String item : pathArr) {
            if (StringUtils.isBlank(item)) {
                continue;
            }

            current += item;

            if (!pathExists(current)) {
                try {
                    this.ftpClient.makeDirectory(current);
                    this.log("make dir", ftpClient.getReplyCode(), ftpClient.getReplyString());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            current += "/";
        }
    }

    /**
     * 目录是否存在
     * @param path 目录.
     * @return 是否存在.
     */
    public boolean pathExists(String path) {
        try {
            ftpClient.changeWorkingDirectory(path);
            String current = ftpClient.printWorkingDirectory();
            return path.equals(current);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public List<String> getFiles(String path) {
        List<String> files = new ArrayList<>();
        try {
            ftpClient.changeWorkingDirectory(path);
            if (this.ftpConfig.getMode().equals(FtpMode.ACTIVE)) {
                ftpClient.enterLocalActiveMode();
            } else {
                ftpClient.enterLocalPassiveMode();
            }
            FTPFile[] ftpFiles = ftpClient.listFiles();
            for (FTPFile ftpFile : ftpFiles) {
                String filename = path + "/" + ftpFile.getName();
                if (path.equals(filename)) {
                    continue;
                }
                if (ftpFile.isDirectory()) {
                    List<String> moreFiles = this.getFiles(filename);
                    files.addAll(moreFiles);
                } else {
                    files.add(filename);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return files;
    }

    /**
     * 记录日志.
     * @param active 操作名称.
     * @param resultCode 操作结果编码.
     * @param resultMessage 操作结果说胆.
     */
    private void log(String active, Integer resultCode, String resultMessage) {
        if (null != ftpLogger) {
            ftpLogger.write(active, resultCode, resultMessage);
        }
    }

    public void remove(String filename) {
        try {
            ftpClient.changeWorkingDirectory(ftpConfig.getFtpRoot());
            if (this.ftpConfig.getMode().equals(FtpMode.ACTIVE)) {
                ftpClient.enterLocalActiveMode();
            } else {
                ftpClient.enterLocalPassiveMode();
            }
            ftpClient.deleteFile(filename);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String read(String filename) {
        InputStream is = null;
        BufferedReader br = null;
        try {
            ftpClient.changeWorkingDirectory(ftpConfig.getFtpRoot());
            if (this.ftpConfig.getMode().equals(FtpMode.ACTIVE)) {
                ftpClient.enterLocalActiveMode();
            } else {
                ftpClient.enterLocalPassiveMode();
            }
            is = ftpClient.retrieveFileStream(filename);
            if (null == is) {
                return null;
            }

            String content = "";
            String str = "";
            br = new BufferedReader(new InputStreamReader(is));
            while((str=br.readLine())!=null){
                content += ("".equals(content) ? "" : "\n") + str;
            }
            ftpClient.completePendingCommand();
            return content;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }

    public void move(String source, String target) {
        String dir = target.substring(0, target.lastIndexOf("/"));
        String filename = target.substring(target.lastIndexOf("/")+1, target.length());
        String content = read(source);
        if (null == content) {
            return;
        }
        write(dir, filename, content);
        remove(source);
    }

    public static void main(String[] args) {
        FtpTest ftpTest = new FtpTest();
        FtpClientUtils clientUtils = new FtpClientUtils(ftpTest);
        //String c1 = clientUtils.read("/lzy19.com_0_1620642807773.xml");
        //System.out.println(c1);

        List<String> files = clientUtils.getFiles("/11");
        System.out.println(files);
        List<String> files2 = clientUtils.getFiles("/11");
        System.out.println(files2);
        //clientUtils.remove("/lzy19.com_0_1620642624967.xml");
        String content = clientUtils.read(files.get(0));
        System.out.println(content);

        clientUtils.move("/lzy19.com_0_1620642807773.xml", "/11-bak/a.xml");
        clientUtils.disconnect();
    }
}
